
#ifndef __CORE_STREAM_H__
#define __CORE_STREAM_H__
#include <string>
//#include <assert.h>
#define BUFFER_ORGINAL_SIZE 8192

class CoreStream
{
public:
	CoreStream() :_buffer(nullptr), _write_pos(0), _read_pos(0), _content_size(0), _buffer_size(BUFFER_ORGINAL_SIZE), _mark_pos(0)
	{
		this->_buffer = new char[_buffer_size];
	}

	CoreStream(int n_capacity) :_buffer(nullptr), _write_pos(0), _read_pos(0), _content_size(0), _mark_pos(0)
	{
		this->_buffer_size = n_capacity;
		this->_buffer = new char[n_capacity];
	}

	CoreStream(char* pdata, int len) :_buffer(nullptr), _read_pos(0), _mark_pos(0)
	{
		this->_buffer_size = len;
		this->_write_pos = len;
		this->_content_size = len;
		this->_buffer = new char[len];
		memcpy(this->_buffer, pdata, len);
	}

	virtual ~CoreStream()
	{
		if (this->_buffer)
		{
			delete this->_buffer;
			this->_buffer = nullptr;
		}
	}

	CoreStream * copy()
	{
		CoreStream * buffer = nullptr;
		if (this->_content_size > 0)
		{
			buffer = new CoreStream(this->_buffer, this->_content_size);
		}
		return buffer;
	}

	unsigned int getWriterIndex() const
	{
		return this->_write_pos;
	}

	unsigned int getContentSize() const
	{
		return this->_content_size;
	}

	unsigned int getReadableSize() const
	{
		return this->_content_size - this->_read_pos;
	}

	unsigned int getReaderIndex() const
	{
		return this->_read_pos;
	}

	unsigned int getBufferSize() const
	{
		return this->_buffer_size;
	}

	void discardReadContent()
	{
		if (this->_read_pos > 0)
		{
			this->moveLeft(this->_read_pos);
		}
	}

	void skipData(unsigned int len)
	{
		if (this->_read_pos + len <= this->_content_size)
		{
			this->_read_pos += len;
		}
		else
		{
			this->_read_pos += (this->_content_size - this->_read_pos);
		}
	}

	void moveWriterIndexToFront()
	{
		this->_write_pos = 0;
	}

	void moveWriterIndexToBack()
	{
		this->_write_pos = this->_content_size;
	}

	void moveReaderIndexToFront()
	{
		this->_read_pos = 0;
	}

	void moveReaderIndexToBack()
	{
		this->_read_pos = this->_content_size;
	}

	void reset()
	{
		this->moveReaderIndexToFront();
		this->moveWriterIndexToFront();
		this->_mark_pos = 0;
		this->_content_size = 0;
	}

	void markReaderIndex()
	{
		this->_mark_pos = this->_read_pos;
	}

	void resetReaderIndex()
	{
		this->_read_pos = this->_mark_pos;
	}

	void setReaderIndex(unsigned int pos)
	{
		if (pos <= this->_content_size)
		{
			this->_read_pos = pos;
		}
	}

	void setWriterIndex(unsigned int pos)
	{
		if (pos <= this->_content_size)
		{
			this->_write_pos = pos;
		}
	}

	void moveLeft(unsigned int len)
	{
		if (this->_content_size == 0)
		{
			return;
		}
		//assert(len != 0, "moveLeft len = 0");
		if (len >= this->_content_size)
		{
			this->clear();
		}
		else
		{
			for (unsigned int i = len; i < this->_content_size; i++)
			{
				*(this->_buffer + i - len) = *(this->_buffer + i);
				*(this->_buffer + i) = 0;
			}
			this->_write_pos = this->_write_pos - len ? this->_write_pos - len > 0 : 0;
			this->_read_pos = this->_read_pos - len ? this->_read_pos - len > 0 : 0;
			this->_mark_pos = this->_mark_pos - len ? this->_mark_pos - len > 0 : 0;
			this->_content_size -= len;
		}
	}

	void moveRight(unsigned int len)
	{
		if (this->_content_size == 0)
		{
			return;
		}
		this->reallocBufferSizeInChanged(len);
		for (int i = this->_content_size - 1; i >= 0; i--)
		{
			*(this->_buffer + i + len) = *(this->_buffer + i);
			*(this->_buffer + i) = 0;
		}
		this->_write_pos += len;
		this->_read_pos += len;
		this->_mark_pos += len;
		this->_content_size += len;
	}

	bool isReadable() const
	{
		if (this->_content_size == 0)
		{
			return false;
		}
		return this->_read_pos < this->_content_size;
	}

	bool isReadable(unsigned int len) const
	{
		if (this->_content_size == 0 || len == 0)
		{
			return false;
		}
		return this->_read_pos + len <= this->_content_size;
	}

	unsigned int length() const
	{
		return this->getContentSize();
	}

	unsigned int capacity() const
	{
		return this->getBufferSize();
	}

	bool empty() const
	{
		return this->length() == 0;
	}

	char * data()
	{
		return this->_buffer;
	}

	void clear()
	{
		this->_content_size = 0;
		this->_write_pos = 0;
		this->_read_pos = 0;
		this->_mark_pos = 0;
		memset(this->_buffer, 0, this->_buffer_size);
	}

	int8_t readInt8(int8_t n = 0)
	{
		this->read(n);
		return n;
	}
	int16_t readInt16(int16_t n = 0)
	{
		this->read(n);
		return n;
	}
	int32_t readInt32(int32_t n = 0)
	{
		this->read(n);
		return n;
	}
	uint8_t readUInt8(uint8_t n = 0)
	{
		this->read(n);
		return n;
	}
	uint16_t readUInt16(uint16_t n = 0)
	{
		this->read(n);
		return n;
	}
	uint32_t readUInt32(uint32_t n = 0)
	{
		this->read(n);
		return n;
	}
	float readFloat(float n = 0.0)
	{
		this->read(n);
		return n;
	}
	double readDouble(double n = 0.0)
	{
		this->read(n);
		return n;
	}

	template <typename T>
	uint32_t readArray(T * pArr, uint32_t len)
	{
		uint32_t ar_len = this->readUInt32();
		if (ar_len <= len)
		{
			auto nlen = ar_len * sizeof(T);
			if (this->_read_pos + nlen <= this->_content_size)
			{
				memcpy(pArr, this->_buffer + this->_read_pos, nlen);
				this->_read_pos += int(nlen);
			}
		}
		else
		{
			this->_read_pos -= sizeof(uint32_t);
			ar_len = 0;
		}
		return ar_len;
	}

	std::string readString()
	{
		int len = this->readUInt32();
		//assert(len > 0, "readString() len <= 0");
		std::string str;
		if (this->_read_pos + len <= this->_content_size)
		{
			char * pdata = new char[len + 1];
			memcpy(pdata, this->_buffer + this->_read_pos, len);
			this->_read_pos += len;
			pdata[len] = 0;
			str.assign(pdata);
			delete pdata;
		}
		return str;
	}
	void writeInt8(int8_t n)
	{
		this->write(n);
	}
	void writeInt16(int16_t n)
	{
		this->write(n);
	}
	void writeInt32(int32_t n)
	{
		this->write(n);
	}
	void writeFloat(float n)
	{
		this->write(n);
	}
	void writeDouble(double n)
	{
		this->write(n);
	}
	void writeUInt8(uint8_t n)
	{
		this->write(n);
	}
	void writeUInt16(uint16_t n)
	{
		this->write(n);
	}
	void writeUInt32(uint32_t n)
	{
		this->write(n);
	}

	void writeString(const char* str, uint32_t len)
	{
		this->writeArray(str, len);
	}

	void writeString(const char* str)
	{
		this->writeArray(str, (uint32_t)strlen(str));
	}

	void writeString(std::string& str)
	{
		this->writeArray(str.c_str(), (uint32_t)str.length());
	}

	template <typename T>
	void writeArray(const T* pdata, uint32_t len)
	{
		unsigned int nlen = sizeof(T) * len;
		this->reallocBufferSizeInChanged(nlen + sizeof(uint32_t));
		this->writeUInt32(len);
		memcpy(this->_buffer + this->_write_pos, pdata, nlen);
		this->_write_pos += int(nlen);
		this->_content_size = this->_write_pos > this->_content_size ? this->_write_pos : this->_content_size;
	}

private:
	template <typename T>
	void write(T n)
	{
		uint32_t nlen = sizeof(T);
		this->reallocBufferSizeInChanged(nlen);
		memcpy(this->_buffer + this->_write_pos, &n, nlen);
		this->_write_pos += int(nlen);
		this->_content_size = this->_write_pos > this->_content_size ? this->_write_pos : this->_content_size;
	}

	template <typename T>
	void read(T &n, bool autooffset = true)
	{
		size_t nlen = sizeof(T);
		if (this->_read_pos + nlen <= this->_buffer_size)
		{
			memcpy(&n, this->_buffer + this->_read_pos, nlen);
			if (autooffset)
			{
				this->_read_pos += int(nlen);
			}
		}
	}

protected:
	void reallocBufferSize()
	{
		int buffer_size = this->_buffer_size * 2;
		char* newbuffer = new char[buffer_size];
		memcpy(newbuffer, this->_buffer, this->_buffer_size);
		this->_buffer_size = buffer_size;
		delete this->_buffer;
		this->_buffer = newbuffer;
	}

	void reallocBufferSizeInChanged(uint32_t len)
	{
		while (true)
		{
			if (this->_write_pos + len <= this->_buffer_size)
			{
				break;
			}
			else
			{
				this->reallocBufferSize();
			}
		}
	}

private:
	char* _buffer;
	unsigned int _write_pos;
	unsigned int _read_pos;
	unsigned int _content_size;
	unsigned int _buffer_size;
	unsigned int _mark_pos;

};


#endif